package com.rockcent.mall.lib.aop;

import com.rockcent.common.utils.RequestUtils;
import com.rockcent.mall.lib.domain.RequestLog;
import com.rockcent.mall.lib.domain.UserInfo;
import com.rockcent.mall.lib.dto.MallDto;
import com.rockcent.mall.lib.dto.MerchantDto;
import com.rockcent.mall.lib.dto.UserInfoDto;
import com.rockcent.mall.lib.repository.RequestLogRepository;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.*;

/**
 * Created by hone on 17-4-13.
 */
//@Aspect
//@Component
public class ControllerInterceptor {

    private final Logger log = LoggerFactory.getLogger(ControllerInterceptor.class);

    private static final String LOG_RESULT_SUCCESS = "success";
    private static final String LOG_RESULT_ERROR = "failed";

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired(required = false)
    private HttpServletRequest request;
    @Value("${applicationName}")
    private String appName;
    @Autowired
    private RequestLogRepository requestLogRepository;

    /**
     * 定义拦截规则
     */
    @Pointcut("execution(* com.rockcent.*.controller..*.*(..))")
    private void controllerMethodPointcut(){
    }

    /**
     * 拦截器具体实现
     * @param pjp
     * @return
     */
    @Around("controllerMethodPointcut()")
    public Object Interceptor(ProceedingJoinPoint pjp) throws Throwable {
        long beginTime = System.currentTimeMillis();
        Object result = pjp.proceed();
        try {
            Set<Object> params = new LinkedHashSet<>();
            MethodSignature signature = (MethodSignature) pjp.getSignature();
            Method method = signature.getMethod(); //获取被拦截的方法
            String methodName = method.getName(); //获取被拦截的方法名
            Object[] args = pjp.getArgs();
            log.info("======================>请求方法: {}", methodName);
            log.info("======================>请求参数: {}", Arrays.toString(args));
            String host = "";
            Long userId = null;
            Long mallId = null;
            Long merchantId = null;
            String url = "";
            for (Object arg : args) {
                if (arg instanceof Map<?, ?>) {
                    Map<String, Object> map = (Map<String, Object>) arg;
                    params.add(map);
                } else if (arg instanceof HttpServletRequest) {
                    HttpServletRequest request = (HttpServletRequest) arg;
                    host = RequestUtils.getIpAddress(request);
                    UserInfoDto user = getUserInfoDto(request);
                    if (user != null) {
                        userId = user.getId();
                        if (user.mall != null) {
                            mallId = user.mall.getId();
                        }
                        if (user.merchant != null) {
                            merchantId = user.merchant.getId();
                        }
                    }
                    url = request.getRequestURL().toString();
                    log.info("======================>链接: {}", url);
                    log.info("======================>IP: {}", host);
                    log.info("======================>操作人ID: {}", userId);
                    //获取query string 或 posted form data参数
                    Map<String, String[]> paramMap = request.getParameterMap();
                    if (paramMap != null && paramMap.size() > 0) {
                        params.add(paramMap);
                    }
                } else {
                    //do nothing...
                }
            }
            long costMs = System.currentTimeMillis() - beginTime;
            log.info("======================>{}请求耗时: {}ms", methodName, costMs);
            saveLogInfo(methodName, Arrays.toString(args), host, url, LOG_RESULT_SUCCESS, "",userId,mallId,merchantId);
        } catch (Exception e) {
            log.info("======================>log Interceptor failed");
        }
        return result;
    }

    @AfterThrowing(value = "controllerMethodPointcut()", throwing = "ex")
    public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) {
        try {
            //异常日志记录
            String methodName = joinPoint.getSignature().getName();
            StringBuffer sb = new StringBuffer();
            for (Object parameterValue : joinPoint.getArgs()) {
                if (parameterValue != null) {
                    sb.append(String.valueOf(parameterValue) + ",");
                }
            }
            String params = sb.length() == 0 ? "" : sb.substring(0, sb.length() - 1).toString();
            String host = request == null ? "" : RequestUtils.getIpAddress(request);
            String url = request == null ? "" : request.getRequestURL().toString();
            Long userId = null;
            Long mallId = null;
            Long merchantId = null;
            UserInfoDto user = getUserInfoDto(request);
            if (user != null) {
                userId = user.getId();
                if (user.mall != null) {
                    mallId = user.mall.getId();
                }
                if (user.merchant != null) {
                    merchantId = user.merchant.getId();
                }
            }
            saveLogInfo(methodName, params, host, url, LOG_RESULT_ERROR, ExceptionUtils.getStackTrace(ex), userId, mallId, merchantId);
        } catch (Exception e) {
            log.info("======================>log afterThrowingAdvice failed");
        }
    }

    /**
     * 保存日志
     * @param methodName
     * @param params
     * @param host
     * @param result
     * @param desc
     * @param operatorId
     */
    private void saveLogInfo(String methodName, String params, String host, String url, String result, String desc, Long operatorId, Long mallId, Long merchantId) {
        try {
            RequestLog requestLog = new RequestLog();
            requestLog.setMethodName(methodName);
            requestLog.setParams(params);
            requestLog.setAddress(host);
            requestLog.setUrl(url);
            requestLog.setResult(result);
            requestLog.setDetail(desc);
            if(operatorId!=null) {
                requestLog.setOperatorId(operatorId);
            }
            if(mallId!=null) {
                requestLog.setMallId(mallId);
            }
            if(merchantId!=null) {
                requestLog.setMechantId(merchantId);
            }
            requestLog.setOperateDate(new Date());
            requestLog.setAppName(appName);
            requestLogRepository.save(requestLog);
        } catch (Exception e) {
            log.info("======================>log save failed");
        }
    }

    /**
     * 获取当前用户信息
     * @param request
     * @return
     */
    public UserInfoDto getUserInfoDto(HttpServletRequest request) {
        UserInfoDto userInfoDto = null;
        if(request!=null) {
            userInfoDto = (UserInfoDto) request.getSession().getAttribute(UserInfo.LOGIN_USER);
            if (userInfoDto != null) {
                String domain = RequestUtils.getDomain(request);
                if (StringUtils.isBlank(domain)) {
                    domain = request.getParameter("domain");
                }
                if (StringUtils.isNotBlank(domain)) {
                    MallDto mallDto = null;
                    MerchantDto merchantDto = null;
                    if(userInfoDto.mallMap!=null) {
                        mallDto = userInfoDto.mallMap.get(domain);
                    }
                    if(userInfoDto.merchantMap!=null) {
                        merchantDto = userInfoDto.merchantMap.get(domain);
                    }
                    userInfoDto.mall = mallDto;
                    userInfoDto.merchant = merchantDto;
                }
            }
        }
        return userInfoDto;
    }

}
